#pragma once
#include <string>
#include <map>
#include <vector>
#include <vulkan/vulkan.h>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

class VulkanApplication
{
public:
    VulkanApplication(const char* appName, int width, int height);
    ~VulkanApplication();

    VkInstance getInstance() { return _instance; }
    VkPhysicalDevice getPhysicalDevice() { return _physicalDevice; }
    VkDevice getDevice() { return _logicDevice; }
    VkSurfaceKHR getSurface() { return _surface; }
    VkSwapchainKHR getSwapchain() { return _swapChain; }
    VkCommandBuffer getCommandBuffer() { return _commandBuffer; }
    VkRenderPass getRenderPass() { return _renderPass; }

    std::vector<VkImage>& getSwapchainImages() { return _swapChainImages; }
    std::vector<VkImageView>& getSwapchainImageViews() { return _swapchainImageViews; }
    std::vector<VkFramebuffer>& getSwapchainFramebuffers() { return  _framebuffers; }
    VkSurfaceFormatKHR getSurfaceFormat() { return _surfaceFormat; }
    VkPresentModeKHR getPresentMode() { return _presentMode; }

    VkFence getOrCreateFence(const std::string& name);
    VkSemaphore getOrCreateSemaphore(const std::string& name);
    void submitAndPresent(VkSemaphore waitImage, VkSemaphore waitSubmission,
                          VkFence fenceSubmission, uint32_t imageIndex);
    uint32_t recreateSwapchain(int& w, int& h, VkSemaphore waitImage);

    bool initialized() const { return _initialized; }
    bool shouldClose() const { return glfwWindowShouldClose(_window); }
    void setClosed() { glfwSetWindowShouldClose(_window, 1); }

    void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
    void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
    void resizeCallback(GLFWwindow* window, int width, int height);

private:
    bool createInstance(const char* appName);
    bool obtainPhysicalDevice();
    bool createLogicDevice();
    bool createWindowSurface(const char* appName, int width, int height);
    bool createSwapchain();
    bool createImageViews();
    bool createCommandBuffer();
    bool createRenderPass();
    bool createFrameBuffers(int w, int h);
    void cleanupSwapchain();

    VkInstance _instance;
    VkPhysicalDevice _physicalDevice;
    VkDevice _logicDevice;
    VkSurfaceKHR _surface;
    VkSwapchainKHR _swapChain;
    VkCommandPool _commandPool;
    VkCommandBuffer _commandBuffer;
    VkRenderPass _renderPass;
    GLFWwindow* _window;

    std::map<std::string, VkFence> _fences;
    std::map<std::string, VkSemaphore> _semaphores;
    std::vector<VkImage> _swapChainImages;
    std::vector<VkImageView> _swapchainImageViews;
    std::vector<VkFramebuffer> _framebuffers;
    VkSurfaceFormatKHR _surfaceFormat;
    VkPresentModeKHR _presentMode;
    bool _initialized;
};
